# \source-code\core.py

from typing import Optional  # 引入 Optional 类型
import json
import hashlib
import pathlib
import os
import shutil

from setting import Target_setting
import my_ignore

class Folder_structure:
    def __init__(self, folder_name: str, folder_content: list, level: int):
        self.folder_name = folder_name
        self.folder_content = folder_content
        self.level = level

    def __str__(self, prefix="", is_last=True):
        """使用树状符号输出文件夹结构"""
        connector = "`-- " if is_last else "+-- "
        result = f"{prefix}{connector}{self.folder_name}/\n"
        
        prefix += "    " if is_last else "|   "
        
        for i, item in enumerate(self.folder_content):
            is_item_last = i == len(self.folder_content) - 1
            if isinstance(item, str):
                # 文件
                connector = "`-- " if is_item_last else "+-- "
                result += f"{prefix}{connector}{item}\n"
            elif isinstance(item, Folder_structure):
                # 文件夹，递归调用
                result += item.__str__(prefix, is_item_last)
        return result
        
    def __repr__(self):
        # 根据层级计算缩进
        indent = "    " * self.level
        result = f"{indent}{self.folder_name}(level:{self.level})/\n"
        for item in self.folder_content:
            if isinstance(item, str):
                # 如果是文件，直接添加文件名
                result += f"{indent}    {item}\n"
            elif isinstance(item, Folder_structure):
                # 如果是文件夹，递归调用 __str__ 方法
                result += repr(item)
        return result


def get_structure_and_file(folder_path: pathlib.Path):
    """
    获取指定文件夹的结构和特定后缀名的文件路径，支持 .gitignore 忽略规则
    
    Args:
        folder_path (pathlib.Path): 目标文件夹路径
    
    Returns:
        dict: 包含 'target_file' 列表和 'folder_structure' 的字典
    """
    result = {
        "target_file": [],
        "folder_structure": Folder_structure(folder_path.name, [], 0),
    }

    def _scan_directory(current_path: pathlib.Path, current_folder: Folder_structure):
        """
        递归扫描目录，支持 .gitignore
        """

        ignore = my_ignore.get_ignore(current_path)

        try:
            entries = list(os.scandir(current_path))
        except (PermissionError, OSError):
            print(f"无法访问目录：{current_path}")
            # input("按回车继续程序...")
            # 创建新的文件夹结构对象
            sub_folder = Folder_structure(current_path.name+"(此文件夹无法访问)", [], current_folder.level + 1)
            # 添加到当前文件夹内容
            current_folder.folder_content.append(sub_folder)
            return  # 无法访问目录则跳过

        # 获取目标文件后缀名
        file_extension = Target_setting.file_extension

        for entry in entries:
            entry_path = pathlib.Path(entry.path)
            entry_name = entry.name

            # 检查是否被 .gitignore 忽略
            if ignore != None and my_ignore.is_ignore(ignore, entry_path):
                continue

            if entry.is_file():
                # 检查文件后缀名
                if entry_path.suffix.lower() in file_extension:
                    result["target_file"].append(entry_path)
                # 添加文件到当前文件夹结构
                current_folder.folder_content.append(entry_name)

            elif entry.is_dir():
                # 忽略 .git 目录
                if entry_name == '.git':
                    continue
                
                # 创建新的文件夹结构对象
                sub_folder = Folder_structure(entry_name, [], current_folder.level + 1)
                # 添加到当前文件夹内容
                current_folder.folder_content.append(sub_folder)
                # 递归处理子文件夹
                _scan_directory(entry_path, sub_folder)

    _scan_directory(folder_path, result["folder_structure"])
    print("遍历文件结构和文件列表完成\n")
    return result


def generate_ai_readable_metadata(structure_result: dict, output_dir: Optional[pathlib.Path]):
    """
    生成AI友好的元数据文件。
    
    Args:
        structure_result (dict): get_structure_and_file 函数的返回结果。
        output_dir (pathlib.Path, optional): 元数据文件的输出目录。如果为None，则使用项目根目录。
    """
    folder_struct = structure_result["folder_structure"]
    target_files = structure_result["target_file"]
    
    metadata = {
        "project_structure_str": str(folder_struct),
        "files": []
    }
    

    for file_path in target_files:
        try:
            # 尝试多种编码方式读取文件
            content = None
            for encoding in ['utf-8', 'gbk', 'gb2312', 'latin-1']:
                try:
                    with open(file_path, 'r', encoding=encoding) as f:
                        content = f.read()
                        break  # 成功读取则跳出循环
                except UnicodeDecodeError:
                    continue  # 尝试下一种编码
            
            # 如果所有编码都失败，则跳过该文件
            if content is None:
                print(f"无法读取文件 {file_path}: 所有编码尝试均失败")
                continue
                
            lines = content.splitlines()
            preview = "\n".join(lines[:10])
            
            metadata["files"].append({
                "path": str(file_path),
                "size": os.path.getsize(file_path),
                "checksum": hashlib.md5(content.encode('utf-8')).hexdigest(),
                "preview": preview,
                "line_count": len(lines)
            })
        except Exception as e:
            print(f"无法读取文件 {file_path}: {str(e)}")
    
    if output_dir is not None:
    # 确定输出路径
        if output_dir.name != "AI":
            output_dir = output_dir / "AI"
            if not output_dir.exists():
                output_dir.mkdir(parents=True, exist_ok=True)
                # parents=True 参数会创建所有必要的父目录 exist_ok=True 参数确保如果目录已存在不会抛出异常
        
        metadata_path = output_dir / "project_metadata.json"
        with open(metadata_path, 'w', encoding='utf-8') as f:
            json.dump(metadata, f, indent=2, ensure_ascii=False)

    
    print("元数据文件已生成：", metadata_path)
    return metadata
    # 使用方式
    # result = get_structure_and_file(pathlib.Path("my_project"))
    # metadata = generate_ai_readable_metadata(result, output_dir=pathlib.Path("my_project"))


def copy_file(structure_result: dict, output_dir: Optional[pathlib.Path] = None):
    """
    将 get_structure_and_file 提取的目标文件复制到指定目录，并保存文件夹结构。

    Args:
        structure_result (dict): get_structure_and_file 的输出结果。
                                  应包含 'target_file' 和 'folder_structure' 键。
        output_dir (Optional[pathlib.Path]): 输出基目录。默认为 None。

    Returns:
        int: 0 表示操作成功完成。
             （注意：当前实现忽略了复制过程中的异常，可根据需要增强错误处理）
    """
    project_root_path = pathlib.Path(structure_result['folder_structure'].folder_name).parent.resolve()

    # 确定最终的 AI 目录路径
    final_output_base = output_dir if output_dir else project_root_path
    ai_dir_name = final_output_base.name
    if ai_dir_name != 'AI':
        ai_output_dir = final_output_base / 'AI'
    else:
        ai_output_dir = final_output_base
        
    # 清空或创建 AI 目录
    if ai_output_dir.exists():
         # 清空目录内容
        for item in ai_output_dir.iterdir():
            if item.is_dir():
                shutil.rmtree(item)
            else:
                item.unlink()
    else:
        # 创建目录
        ai_output_dir.mkdir(parents=True, exist_ok=True)
        
    # 复制目标文件
    for src_file_path in structure_result['target_file']:
        try:
            # 检查源文件和目标文件是否相同，避免自我复制
            if src_file_path.parent == ai_output_dir:
                print(f"跳过复制文件 {src_file_path}：源文件和目标文件在同一目录")
                continue

            # 扁平化复制到 AI 目录
            # 检查文件名是否已存在
            dest_file_path = ai_output_dir / src_file_path.name
            i = 1
            while dest_file_path.exists():
                # 添加数字后缀
                base, ext = os.path.splitext(src_file_path.name)
                new_name = f"{base}_copy{i}{ext}"
                dest_file_path = ai_output_dir / new_name
                i += 1

            # 复制文件
            shutil.copy2(src_file_path, dest_file_path)
        except Exception as e:
            print(f"警告：复制文件 {src_file_path} 到 {dest_file_path} 失败: {e}")
            # input("按回车键继续程序...")

            
    # 保存文件夹结构到文本文件
    structure_str = str(structure_result['folder_structure'])
    structure_file_path = ai_output_dir / 'folder_structure.txt'
    try:
        with open(structure_file_path, 'w', encoding='utf-8') as f:
            f.write(structure_str)
    except Exception as e:
        print(f"警告：写入文件夹结构到 {structure_file_path} 失败: {e}")
    print("已复制目标文件并保存文件夹结构。")

    os.startfile(ai_output_dir)
    return 0


def coremain(source_path, output_dir):
    if type(source_path) is str:
        source_path = pathlib.Path(source_path)
    if type(output_dir) is str:
        output_dir = pathlib.Path(output_dir)

    if not source_path.exists():
        print("源路径不存在")
        return 1

    structure_result = get_structure_and_file(source_path)
    copy_file(structure_result, output_dir)
    generate_ai_readable_metadata(structure_result, output_dir)

    print("执行成功")

    return 0